package org.chispa.api.domain;

import java.util.HashMap;

import org.chispa.api.util.BinningNumber;
import org.chispa.api.util.PropertyLoader;

/**
 * @author  bgabor
 */
class SimpleDomainRetriever implements DomainRetriever {
	
	/**
	 * @uml.property  name="numRet"
	 * @uml.associationEnd  
	 */
	private NumberRetriever numRet;
	
	private String uuid;
	
	private static boolean cacheEnabled=true;
	private HashMap<String,BinningNumber> cache;
	private HashMap<String,String> namespaces=new HashMap<String,String>();
	/**
	 * @uml.property  name="listener"
	 * @uml.associationEnd  
	 */
	private DomainCryptoListener listener;
	
	static{
		String ce=PropertyLoader.getProperty(SimpleDomainRetriever.class,"cacheenabled");
		cacheEnabled=Boolean.parseBoolean(ce);
	}
		
	SimpleDomainRetriever(NumberRetriever numRet,String uuid,DomainCryptoListener listener)
	{
		this.numRet=numRet;
		this.uuid=uuid;
		this.listener=listener;
		if(cacheEnabled)
			cache=new HashMap<String,BinningNumber>();
	}
	
	SimpleDomainRetriever(NumberRetriever numRet,String uuid)
	{
		this(numRet,uuid,null);
	}
	
	@Override
	public void setDomainListener(DomainCryptoListener listener)
	{
		this.listener=listener;
	}
	
	@Override
	public void registerNamespace(String fullname) {
		String[]tokens=fullname.split(":");
		registerNamespace(fullname.replaceFirst(tokens[0]+":",""),tokens[0]);
	}

	@Override
	public void registerNamespace(String ns, String prefix) {
		prefix=prefix.replaceFirst(":","");
		ns=ns.replace("<", "").replace(">", "");
		namespaces.put(prefix, ns);
	}

	@Override
	public BinningNumber createNumber(String id, Double number) {
		return doCreateNumber(id,number,false);
	}

	@Override
	public BinningNumber createNumber(String id, Long number) {
		return doCreateNumber(id,number,false);
	}
	
	@SuppressWarnings("unchecked")
	private<T extends Number> BinningNumber doCreateNumber(String id,T number,boolean fullNs)
	{
		String ns;
		String param;
		String[]params=id.split(":");
		if(params.length>2)
		{
			for(int i=1;i<params.length-1;i++)
			{
				params[0]+=":"+params[i];
			}
			params[1]=params[params.length-1];
		}
		if(!fullNs)
			ns=namespaces.get(params[0]);
		else
			ns=params[0];
		param=params[1];
		BinningNumber bin;
		if(cacheEnabled)
		{
			bin=cache.get(ns+":"+param);
			if(bin!=null)
				return bin.cloneFromDefault(number);
		}
		DomainParameter domain=numRet.queryParams(uuid, ns, param);
		int buck;
		T low =null;
		T high = null;
		if(listener==null)
		{
			buck=Integer.parseInt(domain.getBinNumEnc());
			low=(T) domain.parseLow(number.getClass());
			high=(T) domain.parseHigh(number.getClass());
		}
		else
		{
			buck=listener.onBucketNumber(domain.getBinNumEnc());
			low=listener.onNumber(domain.getLowerEnc(),number);
			high=listener.onNumber(domain.getUpperEnc(),number);
		}
		bin=new BinningNumber(number,low,high, buck);
		if(cacheEnabled)
			cache.put(ns+":"+param, bin);
		return bin;
	}

	@Override
	public BinningNumber createDefaultNumber(String id) {
		return createNumber(id,0.0d);
	}

	@Override
	public BinningNumber createNumberForNamespace(String ns, Double number) {
		return doCreateNumber(ns,number,true);
	}

	@Override
	public BinningNumber createNumberForNamespace(String ns, Long number) {
		return doCreateNumber(ns,number,true);
	}

	@Override
	public BinningNumber createDefaultNumberForNamespace(String ns) {
		return createNumberForNamespace(ns,0.0d);
	}

	/**
	 * @deprecated testing only
	 */
	public void addData(DomainParameter data) {
		numRet.addData(data);
	}

	@Override
	public BinningNumber createDefaultLongNumberForNamespace(String ns) {
		return createNumberForNamespace(ns,0L);
	}

}
